package com.heima.article.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.dto.ArticleDto;
import com.heima.article.dto.ArticleHomeDto;
import com.heima.article.dto.ArticleInfoDto;
import com.heima.article.dto.ArticleStreamMessageDto;
import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApArticleContent;
import com.heima.article.entity.ApAuthor;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.IApArticleContentService;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IApAuthorService;
import com.heima.article.task.CreateHtmlTask;
import com.heima.common.dto.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author Wq
 * @since 2022-10-11
 */
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements IApArticleService {

    @Autowired
    private IApArticleContentService apArticleContentService;
    @Autowired
    private CreateHtmlTask createHtmlTask;
    @Autowired
    private IApAuthorService apAuthorService;

    @Override
    public ResponseResult<Long> saveArticle(ArticleDto dto) {
        //查询文章作者名称
        ApAuthor author = apAuthorService.getById(dto.getAuthorId());
        if (author != null) {
            dto.setAuthorName(author.getName());
        }
        //判断是新增还是修改文章
        if (dto.getId() == null) {//新增
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto, apArticle);
            apArticle.setCreatedTime(new Date());
            this.save(apArticle);

            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentService.save(apArticleContent);
            //异步调用 生成html页面
            createHtmlTask.createrHtml(apArticle, dto.getContent());

            return ResponseResult.okResult(apArticle.getId());
        } else {//修改
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto, apArticle);
            apArticle.setCreatedTime(new Date());
            this.updateById(apArticle);
            //先根据ArticleId查询该条数据
            LambdaQueryWrapper<ApArticleContent> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(ApArticleContent::getArticleId, dto.getId());
            ApArticleContent one = apArticleContentService.getOne(wrapper);
            if (one != null) {
                one.setContent(dto.getContent());
                one.setArticleId(apArticle.getId());
                apArticleContentService.updateById(one);
            }

            //异步调用 生成html页面
            createHtmlTask.createrHtml(apArticle, dto.getContent());
            return ResponseResult.okResult(apArticle.getId());
        }
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public ResponseResult load(ArticleHomeDto dto, Integer flag) {//根据标记对应查询
        //参数校验
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getLoaddir() == 1) {
            //从redis中查询热点文章
            Set<String> strings = redisTemplate.boundZSetOps("hot_article_first_page_" + dto.getChannelId())
                    //查到的数据倒叙排列
                    .reverseRange(0, dto.getSize() - 1);
            //解析json 转换成文章对象  并收集成
            List<ApArticle> apArticleList = strings.stream().map(s -> {
                return JSON.parseObject(s, ApArticle.class);
            }).collect(Collectors.toList());

            if (apArticleList != null && apArticleList.size() == dto.getSize()) {
                //返回结果
                return ResponseResult.okResult(apArticleList);
            }
        }
        //非首页查询数据库
        LambdaQueryWrapper<ApArticle> wrapper = new LambdaQueryWrapper<>();
        if (dto.getChannelId() > 0) {
            wrapper.eq(ApArticle::getChannelId, dto.getChannelId());
        }
        //隐藏条件查询未删除 未下架的文章
        wrapper.eq(ApArticle::getIsDown, false)
                .eq(ApArticle::getIsDelete, false)
                //根据发布时间降序查询
                .orderByDesc(ApArticle::getPublishTime);

        if (flag == 0 || flag == 1) {
            wrapper.gt(ApArticle::getPublishTime, dto.getMaxTime());
        } else {
            wrapper.lt(ApArticle::getPublishTime, dto.getMinTime());
        }
        Page<ApArticle> page = this.page(new Page<>(1, dto.getSize()), wrapper);
        return ResponseResult.okResult(page.getRecords());
    }


    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        ApArticle apArticle = this.getById(dto.getArticleId());
        if (apArticle == null || apArticle.getIsDelete() || apArticle.getIsDown()) {
            //如果该文章不存在或者被删除或者被下架 返回错误信息
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        LambdaQueryWrapper<ApArticleContent> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApArticleContent::getArticleId, apArticle.getId());
        ApArticleContent articleContent = apArticleContentService.getOne(wrapper);

        Map map = new HashMap<>();
        map.put("config", apArticle);
        map.put("content", articleContent);
        return ResponseResult.okResult(map);
    }


    /**
     * 计算热点文章存入redis中
     */
    @Override
    public void compute() {
        //先删除redis中保存的数据
        //查询所有以hot_article_first_page开头的key
        Set<String> keys = redisTemplate.keys("hot_article_first_page*");
        //删除
        redisTemplate.delete(keys);

        //查询文章
        LambdaQueryWrapper<ApArticle> wrapper = new LambdaQueryWrapper<>();
        //查询未删除未下架   前三百天 的文章
        Date date = DateTime.now().minusDays(300).toDate();
        wrapper.eq(ApArticle::getIsDelete, false)
                .eq(ApArticle::getIsDown, false)
                .ge(ApArticle::getPublishTime, date);
        List<ApArticle> articleList = this.list(wrapper);

        //计算每个文章的评分
        for (ApArticle apArticle : articleList) {
            Integer score = computeScore(apArticle);

            //向redis中放入稳定的数据
            ApArticle cache = new ApArticle();
            cache.setId(apArticle.getId());
            cache.setTitle(apArticle.getTitle());
            cache.setAuthorId(apArticle.getAuthorId());
            cache.setAuthorName(apArticle.getAuthorName());
            cache.setChannelId(apArticle.getChannelId());
            cache.setChannelName(apArticle.getChannelName());
            cache.setLayout(apArticle.getLayout());
            cache.setFlag(apArticle.getFlag());
            cache.setImages(apArticle.getImages());
            cache.setPublishTime(apArticle.getPublishTime());
            cache.setStaticUrl(apArticle.getStaticUrl());

            //将信息存入redis
            //hot_article_first_page_频道id
            redisTemplate.boundZSetOps("hot_article_first_page_0")
                    .add(JSON.toJSONString(cache), score);
            redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId())
                    .add(JSON.toJSONString(cache), score);
        }

    }

    /**
     * 热更新分数条
     *
     * @param articleStreamMessage 条消息流
     */
    @Override
    public void updateHotArticleScore(ArticleStreamMessageDto articleStreamMessage) {
        Long articleId = articleStreamMessage.getArticleId();
        //查询文章内容
        ApArticle apArticle = this.getById(articleId);

        ApArticle cache = new ApArticle();
        cache.setId(apArticle.getId());
        cache.setTitle(apArticle.getTitle());
        cache.setAuthorId(apArticle.getAuthorId());
        cache.setAuthorName(apArticle.getAuthorName());
        cache.setChannelId(apArticle.getChannelId());
        cache.setChannelName(apArticle.getChannelName());
        cache.setLayout(apArticle.getLayout());
        cache.setFlag(apArticle.getFlag());
        cache.setImages(apArticle.getImages());
        cache.setPublishTime(apArticle.getPublishTime());
        cache.setStaticUrl(apArticle.getStaticUrl());
        //从redis中查询分数
        Double oldScore = redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId())
                .score(JSON.toJSONString(cache));
        //计算新的分值
        Long newScore = computeNewScore(articleStreamMessage);
        if (oldScore != null) {
            //查到了 则更新分值 oldScore + newScore
            redisTemplate.boundZSetOps("hot_article_first_page_0")
                    .add(JSON.toJSONString(cache), oldScore + newScore);
            redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId())
                    .add(JSON.toJSONString(cache), oldScore + newScore);
        } else {
            //没有  新增分数 newScore
            redisTemplate.boundZSetOps("hot_article_first_page_0")
                    .add(JSON.toJSONString(cache), newScore);
            redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId())
                    .add(JSON.toJSONString(cache), newScore);
        }
        //更新article表   likes   collection  comment views
        // update ap_article set likes = likes+ ?,collection = collection+?,comment = comment + ?,views = views+? where id =?
        LambdaUpdateWrapper<ApArticle> wrapper = new LambdaUpdateWrapper<>();
        wrapper.setSql("likes = likes+" + articleStreamMessage.getLike())
                .setSql("collection = collection+" + articleStreamMessage.getCollect())
                .setSql("comment = comment +" + articleStreamMessage.getComment())
                .setSql("views = views+" + articleStreamMessage.getView())
                .eq(ApArticle::getId, articleId);
        this.update(wrapper);
    }

    /**
     * 计算新分数
     *
     * @param articleStreamMessage 条消息流
     * @return {@link Long}
     */
    private Long computeNewScore(ArticleStreamMessageDto articleStreamMessage) {
        Long score = 0L;
        score += articleStreamMessage.getView() * 1;
        score += articleStreamMessage.getLike() * 3;
        score += articleStreamMessage.getComment() * 5;
        score += articleStreamMessage.getCollect() * 8;
        return score * 3;

    }

    /**
     * 计算分数
     *
     * @param apArticle 美联社文章
     * @return {@link Integer}
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        if (ObjectUtil.isAllNotEmpty(apArticle.getViews(), apArticle.getLikes(), apArticle.getLikes(), apArticle.getCollection())) {
            score += apArticle.getViews() * 1;
            score += apArticle.getLikes() * 3;
            score += apArticle.getComment() * 5;
            score += apArticle.getCollection() * 8;
        }
        return score;
    }
}
